//package morenoapp.model.sociogramm.impl;
//
//import morenoapp.model.sociogramm.*;
//import java.lang.reflect.InvocationHandler;
//import java.lang.reflect.Method;
//import java.lang.reflect.Proxy;
//import java.util.HashMap;
//import java.util.HashSet;
//import java.util.Map;
//import java.util.Set;
//import morenoapp.components.thinkgraphv2.listeners.DisplayConfigChangeListener;
//import morenoapp.model.face.MorenoIdFace;
//import org.json.JSONException;
//import org.json.JSONObject;
//
///**
// *
// * @author dmitry.mamonov
// */
//public class MorenoSociogrammConfigFactory {
//    public static MorenoSociogrammConfig createDisplayConfig() {
//        return (MorenoSociogrammConfig) Proxy.newProxyInstance(MorenoSociogrammConfigFactory.class.getClassLoader(),
//                new Class<?>[]{MorenoSociogrammConfig.class},
//                new MasterInvocationHandler());
//    }
//
//    private static abstract class AbstractInvocationHandler implements InvocationHandler, CommonDisplayConfig {
//        Set<DisplayConfigChangeListener> listeners = new HashSet();
//
//        public void addChangeListener(DisplayConfigChangeListener listener) {
//            listeners.add(listener);
//        }
//
//        public void removeChangeListener(DisplayConfigChangeListener listener) {
//            listeners.remove(listener);
//        }
//
//        protected void fireChanges() {
//            for (DisplayConfigChangeListener lst : listeners) {
//                lst.onDisplayConfigChanged();
//            }
//        }
//    }
//
//    private static class MasterInvocationHandler
//            extends AbstractInvocationHandler
//            implements DisplayConfigChangeListener {
//        Map<Class<?>, Object> delegatesMap = new HashMap();
//
//        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//            //обработка методов непосредственно объявленных в интерфейсе контейнере.
//            if (method.getDeclaringClass() == MorenoSociogrammConfig.class) {
//                //вернуть один из делегатов реализующих частичную конфигурацию.
//                if (method.getAnnotation(GetDisplayConfigDelegate.class) != null) {
//                    if (CommonDisplayConfig.class.isAssignableFrom(method.getReturnType()) && method.getReturnType().isInterface() == true) {
//                        return getDelegate(method.getReturnType());
//                    } else {
//                        throw new RuntimeException(method + " is markered with " + GetDisplayConfigDelegate.class +
//                                "\n so it's return type  must be interface" +
//                                "\n and return type must implement " + CommonDisplayConfig.class);
//                    }
//                }
//            }
//
//            //обработка методов объявленных к конревом интерфесе.
//            if (method.getDeclaringClass() == CommonDisplayConfig.class || method.getDeclaringClass()==Object.class) {
//                return method.invoke(this, args);
//            }
//
//            //делегирование методов
//            if (CommonDisplayConfig.class.isAssignableFrom(method.getDeclaringClass())) {
//                Object delegate = getDelegate(method.getDeclaringClass());
//                return method.invoke(delegate, args);
//            }
//
//            throw new RuntimeException("Unsupported method: " + method + " (declaring class: " + method.getDeclaringClass() + ")");
//        }
//
//        protected Object getDelegate(Class<?> delegateType) {
//            if (delegatesMap.containsKey(delegateType) == false) {
//                delegatesMap.put(delegateType,
//                        Proxy.newProxyInstance(MorenoSociogrammConfigFactory.class.getClassLoader(),
//                        new Class<?>[]{delegateType},
//                        new DelegateInvocationHandler()));
//            }
//            return delegatesMap.get(delegateType);
//        }
//
//        /**
//         * Наследуем оповещение о событии изменения конфигурации от делегатов.
//         */
//        public void onDisplayConfigChanged() {
//            fireChanges();
//        }
//
//        public boolean isChanged() {
//            for (Object proxy : delegatesMap.values()) {
//                DelegateInvocationHandler delegate = (DelegateInvocationHandler) Proxy.getInvocationHandler(proxy);
//                if (delegate.isChanged() == true) {
//                    return true;
//                }
//            }
//            return false;
//        }
//
//        public void resetChangedFlag() {
//            for (Object proxy : delegatesMap.values()) {
//                DelegateInvocationHandler delegate = (DelegateInvocationHandler) Proxy.getInvocationHandler(proxy);
//                delegate.resetChangedFlag();
//            }
//        }
//    }
//
//    private static class DelegateInvocationHandler extends AbstractInvocationHandler {
//        boolean changed = false;
//        JSONObject json = new JSONObject();
//
//        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//            //вызван метод базового интерфейса, используем прямую реализацию.
//            if (method.getDeclaringClass() == CommonDisplayConfig.class || method.getDeclaringClass()==Object.class) {
//                return method.invoke(this, args);
//            }
//
//            //получение значения свойства
//            String name = method.getName();
//            boolean getter;
//            String property;
//            if (name.startsWith("get")) {
//                getter = true;
//                property = name.substring(3).toLowerCase();
//            } else if (name.startsWith("is")) {
//                getter = true;
//                property = name.substring(2).toLowerCase();
//            } else if (name.startsWith("set")) {
//                getter = false;
//                property = name.substring(3).toLowerCase();
//            } else {
//                throw new RuntimeException("Unsupported method: " + method);
//            }
//
//            if (getter == true) {
//                if (args == null || args.length == 0) {
//                    return getProperty(method.getReturnType(), property);
//                } else if (args.length == 1) {
//                    Object objKey = args[0];
//                    if (objKey instanceof Enum) {
//                        return getPropertyOfMap(method.getReturnType(), property, (Enum) objKey);
//                    } else if (objKey instanceof MorenoIdFace){
//                        return getPropertyOfMap(method.getReturnType(), property, (MorenoIdFace) objKey);
//                    } else {
//                        throw new RuntimeException("Argment of " + method + " must be enum type");
//                    }
//                } else {
//                    throw new RuntimeException("Too many arguments in " + method);
//                }
//            } else {
//                if (method.getReturnType() != void.class) {
//                    throw new RuntimeException("Setter must be void type, but passed (return type "+method.getReturnType()+") mthod" + method);
//                }
//                if (args.length == 1) {
//                    setProperty(method.getParameterTypes()[0], property, args[0]);
//                } else if (args.length == 2) {
//                    Object objKey = args[0];
//                    Object value = args[1];
//                    Class<?> valueType = method.getParameterTypes()[1];
//                    if (objKey instanceof Enum) {
//                        setPropertyOfMap(valueType, property, (Enum) objKey, value);
//                    } else if (objKey instanceof MorenoIdFace) {
//                        setPropertyOfMap(valueType, property, (MorenoIdFace) objKey, value);
//                    } else {
//                        throw new RuntimeException("First argument must be enum type for 2 arguments setter, but " + method);
//                    }
//                } else {
//                    throw new RuntimeException("Only 1 or 2 arguments is suported for setter, but " + method);
//                }
//
//                fireChanges();
//
//                return null; //hope is't correct for void return type.
//            }
//        }
//
//        public boolean isChanged() {
//            return changed;
//        }
//
//        public void resetChangedFlag() {
//            changed = false;
//        }
//
//        @Override
//        protected void fireChanges() {
//            changed = true;
//            super.fireChanges();
//        }
//
//        protected Object getProperty(Class<?> type, String property) {
//            try {
//                if (type == boolean.class) {
//                    if (json.has(property) == false) {
//                        json.put(property, false);
//                    }
//                    return json.getBoolean(property);
//
//                }
//                if (type == int.class) {
//
//                    if (json.has(property) == false) {
//                        json.put(property, 0);
//                    }
//                    return json.getInt(property);
//                }
//                if (type == String.class) {
//                    if (json.has(property) == false) {
//                        json.put(property, "");
//                    }
//                    return json.getString(property);
//                }
//                throw new RuntimeException("Unsupported property type " + type + " on property " + property);
//            } catch (JSONException ex) {
//                throw new RuntimeException("Shold not happen", ex);
//            }
//        }
//
//        protected boolean getPropertyOfMap(Class<?> type, String property, Enum key) {
//            try {
//                if (type == boolean.class) {
//                    if (json.has(property) == false) {
//                        json.put(property, new JSONObject());
//                    }
//                    JSONObject map = json.getJSONObject(property);
//                    if (map.has(key.name()) == false) {
//                        map.put(key.name(), false);
//                    }
//                    return map.getBoolean(key.name());
//                }
//                throw new RuntimeException("Unsupported property type " + type + " on property " + property);
//            } catch (JSONException ex) {
//                throw new RuntimeException("Shold not happen", ex);
//            }
//        }
//
//        protected double getPropertyOfMap(Class<?> type, String property, MorenoIdFace key) {
//            try {
//                if (type == double.class) {
//                    if (json.has(property) == false) {
//                        json.put(property, new JSONObject());
//                    }
//                    JSONObject map = json.getJSONObject(property);
//                    String keyName = String.valueOf(key.getId());
//                    if (map.has(keyName) == false) {
//                        map.put(keyName, 0.0);
//                    }
//                    return map.getDouble(keyName);
//                }
//                throw new RuntimeException("Unsupported property type " + type + " on property " + property);
//            } catch (JSONException ex) {
//                throw new RuntimeException("Shold not happen", ex);
//            }
//        }
//
//
//        protected void setProperty(Class<?> type, String property, Object value) {
//            try {
//                if (type == boolean.class || type == int.class || type == String.class) {
//                    json.put(property, value);
//                } else {
//                    throw new RuntimeException("Unsupported property type " + type + " on property " + property);
//                }
//            } catch (JSONException ex) {
//                throw new RuntimeException("Shold not happen", ex);
//            }
//        }
//
//        protected void setPropertyOfMap(Class<?> type, String property, Enum key, Object value) {
//            try {
//                if (type == boolean.class) {
//                    if (json.has(property) == false) {
//                        json.put(property, new JSONObject());
//                    }
//                    JSONObject map = json.getJSONObject(property);
//                    map.put(key.name(), value);
//                } else {
//                    throw new RuntimeException("Unsupported property type " + type + " on property " + property);
//                }
//            } catch (JSONException ex) {
//                throw new RuntimeException("Shold not happen", ex);
//            }
//        }
//
//        protected void setPropertyOfMap(Class<?> type, String property, MorenoIdFace key, Object value) {
//            try {
//                if (type == double.class) {
//                    if (json.has(property) == false) {
//                        json.put(property, new JSONObject());
//                    }
//                    JSONObject map = json.getJSONObject(property);
//                    String keyName = String.valueOf(key.getId());
//                    map.put(keyName, value);
//                } else {
//                    throw new RuntimeException("Unsupported property type " + type + " on property " + property);
//                }
//            } catch (JSONException ex) {
//                throw new RuntimeException("Shold not happen", ex);
//            }
//        }
//
//
//        @Override
//        public boolean equals(Object obj) {
//            if (obj == null) {
//                return false;
//            }
//            if (getClass() != obj.getClass()) {
//                return false;
//            }
//            final DelegateInvocationHandler other = (DelegateInvocationHandler) obj;
//            if (this.json != other.json && (this.json == null || !this.json.equals(other.json))) {
//                return false;
//            }
//            return true;
//        }
//
//        @Override
//        public int hashCode() {
//            int hash = 7;
//            hash = 89 * hash + (this.json != null ? this.json.hashCode() : 0);
//            return hash;
//        }
//
//
//    }
//}
